package cn.jolyoulu.cmnservice.serivce.impl;

import cn.jolyoulu.cmnservice.enums.CmnExpType;
import cn.jolyoulu.common.redis.utils.RedisUtils;
import cn.jolyoulu.common.web.enums.GlobalExpType;
import cn.jolyoulu.common.web.excption.GlobalException;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import org.springframework.beans.factory.annotation.Autowired;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import cn.jolyoulu.cmnservice.constant.ServiceRedisConstant;
import cn.jolyoulu.cmnservice.dao.SysMenuMapper;
import cn.jolyoulu.cmnservice.entity.dto.AddMenuDTO;
import cn.jolyoulu.cmnservice.entity.dto.TreeDTO;
import cn.jolyoulu.cmnservice.entity.po.SysMenu;
import cn.jolyoulu.cmnservice.serivce.SysMenuService;

import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.stream.Collectors;

@Service
public class SysMenuServiceImpl extends ServiceImpl<SysMenuMapper, SysMenu> implements SysMenuService {

    @Autowired
    private SysMenuMapper sysMenuMapper;

    @Autowired
    private SysMenuService sysMenuService;

    @Autowired
    private RedisUtils redisUtil;

    @Override
    public Set<String> selectPermissionMenuByRoleId(String roleId) {
        String key = ServiceRedisConstant.getRolePermission(roleId);
        Set<String> cache = (Set<String>) redisUtil.get(key);
        if (Objects.isNull(cache)){
            cache = sysMenuMapper.selectPermissionMenuByRoleId(roleId);
            if (Objects.nonNull(cache)){
                redisUtil.set(key,cache,10, TimeUnit.DAYS);
            }
        }
        return cache;
    }

    @Override
    public List<SysMenu> selectByParentId(String parentId) {
        LambdaQueryWrapper<SysMenu> ew = new LambdaQueryWrapper<>();
        ew.eq(SysMenu::getParentId, parentId);
        ew.orderByDesc(SysMenu::getTreeSort);
        return sysMenuMapper.selectList(ew);
    }

    @Override
    public <R extends TreeDTO<R>> List<R> selectTree(String parentId, Function<SysMenu, R> format) {
        //获取数据
        List<SysMenu> sysMenus = sysMenuService.selectByParentId(parentId);
        //如果空就返回
        if (Objects.isNull(sysMenus) || sysMenus.isEmpty()) {
            return null;
        }
        //遍历数据格式化
        ArrayList<R> res = new ArrayList<>();
        for (SysMenu item : sysMenus) {
            R apply = format.apply(item);
            res.add(apply);
            if (item.getTreeLeaf().equals(0)) {
                apply.setChildren(selectTree(item.getId(), format));
            }
        }
        return res;
    }

    @Override
    public <R extends TreeDTO<R>> List<R> selectRolesMenuTree(Set<String> roles, String parentId, Function<SysMenu, R> format) {
        //获取数据
        List<SysMenu> sysMenus = sysMenuService.selectByRolesAndParentId(roles, parentId);
        //如果空就返回
        if (Objects.isNull(sysMenus) || sysMenus.isEmpty()) {
            return null;
        }
        //遍历数据格式化
        ArrayList<R> res = new ArrayList<>();
        for (SysMenu item : sysMenus) {
            R apply = format.apply(item);
            res.add(apply);
            if (item.getTreeLeaf().equals(0)) {
                apply.setChildren(selectRolesMenuTree(roles, item.getId(), format));
            }
        }
        return res;
    }

    @Override
    @Transactional
    public Boolean addLeafMenu(AddMenuDTO dto) {
        SysMenu sysMenu = dto.toSysMenu();
        //获取父节点信息
        SysMenu parent = sysMenuService.getById(sysMenu.getParentId());
        if (Objects.isNull(parent)) {
            throw new GlobalException(CmnExpType.SYSMENU_PARENT_NOT_EXIST);
        }
        sysMenu.setParentId(parent.getId());
        sysMenu.setParentIds(parent.getParentIds() + "," + parent.getId());
        sysMenu.setTreeLeaf(1);
        sysMenu.setTreeLevel(parent.getTreeLevel() + 1);
        sysMenu.setTreeNames(parent.getTreeNames() + "/" + sysMenu.getMenuName());
        //更新父节点非子节点
        if (!sysMenuService.updateById(new SysMenu().setId(parent.getId()).setTreeLeaf(0))) {
            throw new GlobalException(CmnExpType.SYSMENU_ADD_ERROR);
        }
        return sysMenuService.save(sysMenu);
    }

    @Override
    @Transactional
    public Boolean addRootMenu(AddMenuDTO dto) {
        SysMenu sysMenu = dto.toSysMenu();
        sysMenu.setParentId("0");
        sysMenu.setParentIds("0");
        sysMenu.setTreeLeaf(1);
        sysMenu.setTreeLevel(0);
        sysMenu.setTreeNames(sysMenu.getMenuName());
        return sysMenuService.save(sysMenu);
    }

    @Override
    @Transactional
    public void updateParent(String id, String newParentId) {
        SysMenu menu = sysMenuService.getById(id);
        String oldParentId = menu.getParentId();
        //新父节点信息
        SysMenu newPatent = sysMenuService.getById(newParentId);
        if (Objects.isNull(newPatent)) {
            throw new GlobalException(CmnExpType.SYSMENU_PARENT_NOT_EXIST);
        }
        //修改节点到父节点下
        SysMenu updateMenu = new SysMenu();
        updateMenu.setId(menu.getId());
        updateMenu.setParentId(newPatent.getId());
        updateMenu.setParentIds(newPatent.getParentIds() + "," + newPatent.getId());
        updateMenu.setTreeLevel(newPatent.getTreeLevel() + 1);
        updateMenu.setTreeNames(newPatent.getTreeNames() + "/" + menu.getMenuName());
        if (!sysMenuService.updateById(updateMenu)) {
            throw new GlobalException(CmnExpType.SYSMENU_EDIT_ERROR);
        }
        //修改新父节点未非叶子节点
        SysMenu updateNewPatent = new SysMenu();
        updateNewPatent.setId(newPatent.getId());
        updateNewPatent.setTreeLeaf(0);
        if (!sysMenuService.updateById(updateNewPatent)) {
            throw new GlobalException(CmnExpType.SYSMENU_EDIT_ERROR);
        }
        //判断旧节点是否下是否还有子节点(没有需要设置为叶子节点)
        Integer parentCount = sysMenuService.countByParentId(menu.getParentId());
        if (Objects.isNull(parentCount) || parentCount == 0) {
            SysMenu updateOldParent = new SysMenu();
            updateOldParent.setId(oldParentId);
            updateOldParent.setTreeLeaf(1);
            if (!sysMenuService.updateById(updateOldParent)) {
                throw new GlobalException(CmnExpType.SYSMENU_EDIT_ERROR);
            }
        }
        //判断当前节点下是否有子节点
        Integer childCount = sysMenuService.countByParentId(id);
        if (!Objects.isNull(childCount) && parentCount > 0) {
            sysMenuService.updateChildParent(id);
        }
    }

    @Override
    @Transactional
    public void updateChildParent(String id) {
        SysMenu parent = sysMenuService.getById(id);
        List<SysMenu> childList = sysMenuService.selectByParentId(id);
        for (SysMenu menu : childList) {
            sysMenuService.updateParent(menu.getId(), parent.getId());
        }
    }

    @Override
    public Integer countByParentId(String parentId) {
        LambdaQueryWrapper<SysMenu> ew = new LambdaQueryWrapper<>();
        ew.eq(SysMenu::getParentId, parentId);
        return Math.toIntExact(sysMenuMapper.selectCount(ew));
    }

    @Override
    @Transactional
    public void delById(String id) {
        //检查是否有子菜单
        List<SysMenu> child = sysMenuService.selectByParentId(id);
        if (Objects.nonNull(child) && !child.isEmpty()) {
            throw new GlobalException(CmnExpType.SYSMENU_HAVE_CHILD_DEL_ERROR);
        }
        //检查是否有角色关联
        Integer count = sysMenuMapper.countRoleId(id);
        if (Objects.nonNull(count) && count > 0) {
            throw new GlobalException(CmnExpType.SYSMENU_JOIN_ROLE_DEL_ERROR);
        }
        sysMenuMapper.deleteById(id);
    }

    @Override
    public List<SysMenu> selectByRolesAndParentId(Set<String> roleIds, String parentId) {
        QueryWrapper<Object> ew = new QueryWrapper<>();
        ew.in("srm.role_id", roleIds);
        ew.eq("sm.parent_id", parentId);
        ew.eq("sm.menu_type", 1);
        ew.eq("sm.del_flag", 0);
        ew.eq("sm.is_show", 1);
        ew.orderByDesc("sm.tree_sort");
        return sysMenuMapper.selectByRolesAndParentId(ew);
    }

    @Override
    public List<SysMenu> selectByRolesAndLikeParentIds(String roleId, String parentId) {
        QueryWrapper<Object> ew = new QueryWrapper<>();
        ew.eq("srm.role_id", roleId);
        ew.likeRight("sm.parent_ids", parentId);
        ew.eq("sm.del_flag", 0);
        ew.orderByDesc("sm.tree_sort");
        return sysMenuMapper.selectByRolesAndParentId(ew);
    }

    @Override
    public List<String> selectIdByRoleId(String roleId) {
        List<SysMenu> sysMenus = sysMenuService.selectByRolesAndLikeParentIds(roleId, "0");
        return sysMenus.stream().map(SysMenu::getId).collect(Collectors.toList());
    }
}